package com.sz.netty.server.handler;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.sz.netty.entity.DtuFtpMessageLog;
import com.sz.netty.entity.DtuFtpMessageDetailCard;
import com.sz.netty.entity.DtuFtpCardInfo;
import com.sz.netty.service.DtuFtpMessageLogService;
import com.sz.netty.service.DtuFtpMessageDetailCardService;
import com.sz.netty.service.DtuFtpCardInfoService;

import java.util.Arrays;
import java.util.Date;

/**
 * Handler for processing card data with the specified formats:
 * Type 1: 0x040C02200004008485266B9D, 0x040C02200004008D24FD0A8F
 * Type 2: 0x040F022000440004710F1F2A028956
 * 
 * Both start with 0x040C and 0x040F
 */
@Slf4j
@Component
public class CardDataHandler extends SimpleChannelInboundHandler<byte[]> {
    
    // Autowired services for database operations
    private static DtuFtpMessageLogService dtuFtpMessageLogService;
    private static DtuFtpMessageDetailCardService dtuFtpMessageDetailCardService;
    private static DtuFtpCardInfoService dtuFtpCardInfoService;
    
    @Autowired
    public void setDtuFtpMessageLogService(DtuFtpMessageLogService dtuFtpMessageLogService) {
        CardDataHandler.dtuFtpMessageLogService = dtuFtpMessageLogService;
    }
    
    @Autowired
    public void setDtuFtpMessageDetailCardService(DtuFtpMessageDetailCardService dtuFtpMessageDetailCardService) {
        CardDataHandler.dtuFtpMessageDetailCardService = dtuFtpMessageDetailCardService;
    }
    
    @Autowired
    public void setDtuFtpCardInfoService(DtuFtpCardInfoService dtuFtpCardInfoService) {
        CardDataHandler.dtuFtpCardInfoService = dtuFtpCardInfoService;
    }
    
    // Command types
    private static final byte[] COMMAND_TYPE_1 = {(byte) 0x04, (byte) 0x0C};
    private static final byte[] COMMAND_TYPE_2 = {(byte) 0x04, (byte) 0x0F};
    
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, byte[] msg) throws Exception {
        // Log received raw data
        log.debug("CardDataHandler received raw data: {}", bytesToHex(msg));
        log.info("CardDataHandler received {} bytes of raw data", msg.length);
        
        // Check if this is a card data packet we need to process
        if (isCardDataPacket(msg)) {
            log.info("Processing card data packet: {}", bytesToHex(msg));
            
            // Process the card data packet
            processCardDataPacket(msg);
        } else {
            // If not a card data packet, pass it to the next handler in the pipeline
            ctx.fireChannelRead(msg);
        }
    }
    
    /**
     * Check if the received data is a card data packet
     * @param data the received data
     * @return true if it's a card data packet, false otherwise
     */
    private boolean isCardDataPacket(byte[] data) {
        // Check minimum length
        if (data.length < 2) {
            return false;
        }
        
        // Check if it starts with 0x040C or 0x040F
        return (data[0] == COMMAND_TYPE_1[0] && data[1] == COMMAND_TYPE_1[1]) ||
               (data[0] == COMMAND_TYPE_2[0] && data[1] == COMMAND_TYPE_2[1]);
    }
    
    /**
     * Process a card data packet
     * @param packet the card data packet
     */
    private void processCardDataPacket(byte[] packet) {
        try {
            // Save the main message log
            DtuFtpMessageLog messageLog = saveMessageLog(packet);
            
            if (messageLog != null) {
                // Save card message detail
                saveCardMessageDetail(messageLog, packet);
                
                // Save card info (only if it's a new card info)
                saveCardInfo(packet);
            }
        } catch (Exception e) {
            log.error("Error processing card data packet: {}", bytesToHex(packet), e);
        }
    }
    
    /**
     * Save the main message log
     * @param packet the card data packet
     * @return the saved message log entity
     */
    private DtuFtpMessageLog saveMessageLog(byte[] packet) {
        try {
            if (dtuFtpMessageLogService != null) {
                DtuFtpMessageLog messageLog = new DtuFtpMessageLog();
                messageLog.setReceiveMessage(packet);
                
                // Extract command type (first 2 bytes)
                if (packet.length >= 2) {
                    int commandType = ((packet[0] & 0xFF) << 8) | (packet[1] & 0xFF);
                    messageLog.setReceiveMessageType(commandType);
                }
                
                // Extract device number if possible (assuming it's in the packet)
                // For now, we'll set it as "UNKNOWN" and it can be updated later
                messageLog.setDtuNumber("UNKNOWN");
                
                // Save to database
                messageLog = dtuFtpMessageLogService.saveMessageLog(messageLog);
                log.info("Saved card message log to database with ID: {}", messageLog.getId());
                
                return messageLog;
            }
        } catch (Exception e) {
            log.error("Error saving card message log to database: {}", e.getMessage(), e);
        }
        return null;
    }
    
    /**
     * Save card message detail
     * @param messageLog the message log entity
     * @param packet the card data packet
     */
    private void saveCardMessageDetail(DtuFtpMessageLog messageLog, byte[] packet) {
        try {
            if (dtuFtpMessageDetailCardService != null) {
                DtuFtpMessageDetailCard messageDetailCard = new DtuFtpMessageDetailCard();
                messageDetailCard.setFtpMessageLogId(messageLog.getId());
                messageDetailCard.setReceiveMessage(packet);
                
                // Extract card info from packet
                String cardInfo = extractCardInfo(packet);
                messageDetailCard.setCardInfo(cardInfo);
                
                // Set device number if possible
                // For now, we'll set it as "UNKNOWN" and it can be updated later
                messageDetailCard.setDtuNumber("UNKNOWN");
                
                // Save to database
                dtuFtpMessageDetailCardService.saveMessageDetailCard(messageDetailCard);
                log.info("Saved card message detail to database");
            }
        } catch (Exception e) {
            log.error("Error saving card message detail to database: {}", e.getMessage(), e);
        }
    }
    
    /**
     * Save card info (only if it's a new card info)
     * @param packet the card data packet
     */
    private void saveCardInfo(byte[] packet) {
        try {
            if (dtuFtpCardInfoService != null) {
                // Extract card info from packet
                String cardInfo = extractCardInfo(packet);
                
                // Check if this card info already exists
                DtuFtpCardInfo existingCardInfo = dtuFtpCardInfoService.findByCardInfo(cardInfo);
                
                // Only save if it doesn't exist yet
                if (existingCardInfo == null) {
                    DtuFtpCardInfo cardInfoEntity = new DtuFtpCardInfo();
                    cardInfoEntity.setReceiveMessage(packet);
                    cardInfoEntity.setCardInfo(cardInfo);
                    
                    // Extract card number if possible
                    String cardNumber = extractCardNumber(packet);
                    cardInfoEntity.setCardNumber(cardNumber);
                    
                    // Set device number if possible
                    // For now, we'll set it as "UNKNOWN" and it can be updated later
                    cardInfoEntity.setDtuNumber("UNKNOWN");
                    
                    // Save to database
                    dtuFtpCardInfoService.saveCardInfo(cardInfoEntity);
                    log.info("Saved new card info to database: {}", cardInfo);
                } else {
                    log.info("Card info already exists in database, skipping: {}", cardInfo);
                }
            }
        } catch (Exception e) {
            log.error("Error saving card info to database: {}", e.getMessage(), e);
        }
    }
    
    /**
     * Extract card info from packet
     * For packets starting with 0x040C or 0x040F, we'll extract relevant information
     * @param packet the card data packet
     * @return the extracted card info
     */
    private String extractCardInfo(byte[] packet) {
        try {
            // For packet type 1 (0x040C): 0x040C02200004008485266B9D
            // For packet type 2 (0x040F): 0x040F022000440004710F1F2A028956
            
            if (packet.length >= 2) {
                // Check packet type
                if (packet[0] == COMMAND_TYPE_1[0] && packet[1] == COMMAND_TYPE_1[1]) {
                    // Type 1 packet
                    // Extract relevant part as card info (skip first 2 bytes)
                    if (packet.length > 2) {
                        return bytesToHex(Arrays.copyOfRange(packet, 2, packet.length));
                    }
                } else if (packet[0] == COMMAND_TYPE_2[0] && packet[1] == COMMAND_TYPE_2[1]) {
                    // Type 2 packet
                    // Extract relevant part as card info (skip first 2 bytes)
                    if (packet.length > 2) {
                        return bytesToHex(Arrays.copyOfRange(packet, 2, packet.length));
                    }
                }
            }
            
            // Fallback: use the entire packet as card info
            return bytesToHex(packet);
        } catch (Exception e) {
            log.warn("Failed to extract card info from packet: {}", e.getMessage());
            return "UNKNOWN";
        }
    }
    
    /**
     * Extract card number from packet
     * @param packet the card data packet
     * @return the extracted card number
     */
    private String extractCardNumber(byte[] packet) {
        try {
            // For now, we'll use a portion of the packet as card number
            // In a real implementation, this would depend on the actual packet structure
            if (packet.length >= 4) {
                return bytesToHex(Arrays.copyOfRange(packet, 0, 4));
            }
        } catch (Exception e) {
            log.warn("Failed to extract card number from packet: {}", e.getMessage());
        }
        return "UNKNOWN";
    }
    
    /**
     * Convert byte array to hex string
     * @param bytes the byte array
     * @return hex string representation
     */
    private static String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02X", b));
        }
        return sb.toString();
    }
    
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        log.error("Exception in card data handler", cause);
        ctx.close();
    }
}